package com.aplink.generic.widgets;

import java.util.ArrayList;

import android.content.Context;
import android.location.Location;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;

import com.aplink.generic.GenericFragment;
import com.aplink.generic.adapter.GoogleMapInfoWindowAdapter;
import com.aplink.generic.cache.ContentDataManager;
import com.aplink.generic.constant.type.TravelMode;
import com.aplink.generic.dictionary.GenericDictionary;
import com.aplink.generic.google.maps.clustering.ClusterManager;
import com.aplink.generic.google.maps.ui.DirectionUtil;
import com.aplink.generic.google.maps.ui.LocationUtil;
import com.aplink.generic.interfaces.FragmentStateListener;
import com.aplink.generic.interfaces.OnGetObjProInfo;
import com.aplink.generic.interfaces.OnGoogleMapsCameraChangeListener;
import com.aplink.generic.pojo.ColumnSetting;
import com.aplink.generic.pojo.ContentData;
import com.aplink.generic.pojo.GenericAttribute;
import com.aplink.generic.pojo.GenericObject;
import com.aplink.generic.pojo.controlspec.ControlSpec;
import com.aplink.generic.pojo.controlspec.GoogleMapsSpec;
import com.aplink.generic.pojo.googlemaps.DirectionRequest;
import com.aplink.generic.pojo.googlemaps.DirectionsResult;
import com.aplink.generic.task.GetDirectionTask;
import com.aplink.generic.widgets.hacky.MapWrapperLayout;
import com.aplink.generic.xmlparser.ColumnSettingParser;
import com.aplink.sg.R;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapLoadedCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.Circle;
import com.google.android.gms.maps.model.CircleOptions;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

public class ApLinkGoogleMaps extends ApLinkBaseView implements
        LocationListener, OnGoogleMapsCameraChangeListener,
        ClusterManager.OnClusterItemClickListener<GenericObject>,
        Handler.Callback, FragmentStateListener {
	public static final int GET_DIRECTION = 0;

	public static int getPixelsFromDp(final Context context, final float dp) {
		final float scale = context.getResources().getDisplayMetrics().density;
		return (int) (dp * scale + 0.5f);
	}

	private GoogleMapInfoWindowAdapter adapter;
	private boolean isBindingData;
	private final ImageButton mButtonMyLocation;
	private Circle mCircle;
	private ClusterManager<GenericObject> mClusterManager;

	private final ArrayList<ColumnSetting> mColumnSetting;

	private final ContentData mContentData;

	private LatLng mCurrentLocation;

	private ArrayList<GenericObject> mGenericObjects;

	private final GoogleMap mGoogleMap;

	private final GoogleMapsSpec mGoogleMapsSpec;

	private final Handler mHandler;

	private final MapWrapperLayout mMapWrapperLayout;

	private Marker mMarker;
	private final View.OnClickListener mOnMylocationClickListener = new View.OnClickListener() {

		@Override
		public void onClick(final View v) {
			mGoogleMap
			        .animateCamera(CameraUpdateFactory
			                .newCameraPosition(CameraPosition
			                        .builder()
			                        .tilt((float) mGoogleMapsSpec
			                                .getCameraTilt()).zoom(mZoomLevel)
			                        .target(mCurrentLocation).build()));
		}
	};

	private GenericObject mSelectedObject;

	private float mZoomLevel;

	public ApLinkGoogleMaps(final GenericFragment genericFragment,
	        final ControlSpec controlSpec, final View view, Bundle bundle) {
		super(genericFragment, controlSpec, view, bundle);
		mMapWrapperLayout = (MapWrapperLayout) findViewById(R.id.maps_layout);
		mGoogleMap = ((SupportMapFragment) getFragmentManager()
		        .findFragmentById(controlSpec.getIDValue())).getMap();
		mMapWrapperLayout.init(mGoogleMap,
		        getPixelsFromDp(getApplicationContext(), 39 + 20));
		mGoogleMapsSpec = (GoogleMapsSpec) controlSpec;
		mButtonMyLocation = (ImageButton) mMapWrapperLayout
		        .findViewById(R.id.btn_mylocation);
		mContentData = ContentDataManager.getInstance().get(
		        this.mGoogleMapsSpec.getContentDataID());
		mColumnSetting = ColumnSettingParser.parse(getGenericFragment(),
		        mGoogleMapsSpec.getColumnSettingID());
		mHandler = new Handler(this);
	}

	@Override
	public void clear() {

	}

	@Override
	public GenericAttribute getAttribute() {
		return null;
	}

	public LatLng getCurrentLocation() {
		return mCurrentLocation;
	}

	public Marker getCurrentMarker() {
		return mMarker;
	}

	public void getDirection(final Marker marker) {
		final DirectionRequest mDirectionRequest = new DirectionRequest(
		        mCurrentLocation, marker.getPosition(), TravelMode.DRIVING);
		new GetDirectionTask(mHandler).execute(mDirectionRequest);
	}

	@Override
	public GenericDictionary<String, GenericAttribute> getListAttributes() {
		return null;
	}

	public GenericObject getSelectedObject() {
		return mSelectedObject;
	}

	@Override
	public View getView() {
		return null;
	}

	@Override
	public boolean handleMessage(final Message msg) {
		if (msg.what == GET_DIRECTION) {
			DirectionUtil.getInstance().DrawDirectionline(mGoogleMap,
			        (DirectionsResult) msg.obj);
		}
		return false;
	}

	@Override
	protected void initViewProperties() {
		mGoogleMap.setMapType(mGoogleMapsSpec.getGoogleMapsType().getValue());
		mGoogleMap.getUiSettings().setScrollGesturesEnabled(
		        mGoogleMapsSpec.isUiScrollGestures());
		mGoogleMap.getUiSettings().setCompassEnabled(
		        mGoogleMapsSpec.isUiCompass());
		mGoogleMap.getUiSettings().setRotateGesturesEnabled(
		        mGoogleMapsSpec.isUiRotateGestures());
		mGoogleMap.getUiSettings().setZoomControlsEnabled(
		        mGoogleMapsSpec.isUiZoomControls());
		mGoogleMap.getUiSettings().setZoomGesturesEnabled(
		        mGoogleMapsSpec.isUiZoomGestures());
		if (mGoogleMapsSpec.isLoadGPSLocation()) {
			requestLocation();
		}
		mClusterManager = new ClusterManager<>(getApplicationContext(),
		        mGoogleMap);
		mGenericObjects = getDatabaseGet().getList(mContentData, null);
		mClusterManager.addItems(mGenericObjects);
	}

	@Override
	public void languageChanged() {

	}

	@Override
	protected void loadBindingObject() {
		GenericObject selectedObject = null;
		for (GenericObject genericObject : mGenericObjects) {
			if (genericObject.contains(getPassedObject(),
			        mGoogleMapsSpec.getSearchFields())) {
				selectedObject = genericObject;
				break;
			}
		}

		if (selectedObject != null) {
			requestLocation();
			isBindingData = true;
			mGoogleMap.animateCamera(CameraUpdateFactory
			        .newCameraPosition(CameraPosition.builder()
			                .tilt((float) mGoogleMapsSpec.getCameraTilt())
			                .zoom(19f).target(selectedObject.getPosition())
			                .build()));
			mSelectedObject = selectedObject;
		}

		mGoogleMap.setOnMapLoadedCallback(new OnMapLoadedCallback() {

			@Override
			public void onMapLoaded() {
				if (mSelectedObject != null) {
					if (mSelectedObject.getMarker() != null) {
						mSelectedObject.getMarker().showInfoWindow();
					}
				}
			}
		});
	}

	@Override
	protected void loadDefaultObject() {

	}

	@Override
	public void onCameraChange(final CameraPosition position) {
		if (mZoomLevel == 0) {
			mZoomLevel = (float) mGoogleMapsSpec.getCameraZoom();
		} else if (position.zoom != mZoomLevel) {
			mZoomLevel = position.zoom;
		}
	}

	@Override
	public boolean onClusterItemClick(final GenericObject item) {
		mSelectedObject = item;
		return false;
	}

	@Override
	public void onCreate(final Bundle bundle) {

	}

	@Override
	public void onDestroy() {
		LocationUtil.getInstance().dispose();
	}

	@Override
	public void onLocationChanged(final Location location) {
		if (mCurrentLocation == null) {
			mCurrentLocation = new LatLng(location.getLatitude(),
			        location.getLongitude());
			if (!isBindingData) {
				mButtonMyLocation.performClick();
			}
		} else {
			mCurrentLocation = null;
			mCurrentLocation = new LatLng(location.getLatitude(),
			        location.getLongitude());
		}
		setCurrentLocation(location);
	}

	@Override
	public void onPause() {
		LocationUtil.getInstance().disconnect();
	}

	@Override
	public void onResume() {
	}

	@Override
	public void onStart() {

	}

	@Override
	public void onStop() {

	}

	private void requestLocation() {
		LocationUtil.getInstance().builder().setInterval(5)
		        .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
		        .setFastestInterval(5).setLocationListener(this)
		        .initLocationClient(getApplicationContext()).create().connect();
	}

	private void setCurrentLocation(final Location location) {
		final LatLng target = new LatLng(location.getLatitude(),
		        location.getLongitude());
		if (mMarker != null) {
			mMarker.remove();
		}
		if (mCircle != null) {
			mCircle.remove();
		}
		final CircleOptions circleOptions = new CircleOptions();
		circleOptions
		        .fillColor(getResources().getColor(R.color.grid_state_focused))
		        .strokeColor(
		                getResources().getColor(android.R.color.holo_blue_dark))
		        .strokeWidth(1.5f).radius(location.getAccuracy())
		        .center(target);
		final MarkerOptions markerOptions = new MarkerOptions();
		markerOptions
		        .icon(BitmapDescriptorFactory.fromResource(R.drawable.blue_dot))
		        .anchor(0.5F, 0.5F).position(target);
		mMarker = mGoogleMap.addMarker(markerOptions);
		mCircle = mGoogleMap.addCircle(circleOptions);
	}

	@Override
	protected void setEventListener() {
		mGoogleMap.setOnCameraChangeListener(mClusterManager);
		mGoogleMap.setOnMarkerClickListener(mClusterManager);
		mButtonMyLocation.setOnClickListener(mOnMylocationClickListener);
		mClusterManager.setCameraChangeListener(this);
		mClusterManager.setOnClusterItemClickListener(this);
		adapter = new GoogleMapInfoWindowAdapter(this, mColumnSetting,
		        mGoogleMapsSpec.getInfoWindowsDesignID(), mMapWrapperLayout);
		mGoogleMap.setInfoWindowAdapter(adapter);
		getGenericFragment().setFragmentStateListener(this);
	}

	public void setMarker(final Marker marker) {
		mMarker = marker;
	}

	@Override
	public void setObjPropInfoTemp(final OnGetObjProInfo getObjProInfo) {

	}

	@Override
	public void setOnClickListener(final OnClickListener onClickListener) {

	}

	public void setSelectedObject(final GenericObject selectedObject) {
		mSelectedObject = selectedObject;
	}
}
